Skip to main content

第 9 章:引入配置 ConfigMap

要解決的問題

將不同環境(development, staging, production)的部署代碼一併交付在程式碼中,而沒有發覺自己將應用服務暴露在危險之中,一旦其他人也存取代碼,便能知道各個環境的敏感資料

解決方法

將配置環境儲存於環境變數中,降低 Configuration 與程式碼的耦合,減少環境代碼被交付在程式碼的情況。然而,將環境代碼與程式碼分開之後,我們也會面臨到另外一個問題:環境代碼(Configuration)可能通常都散落在各地,必須有個的地方統一管理這些環境代碼。更糟的是,每個環境代碼可能都因應不同的程式語言,有著不同的格式,也使得統一管理更加困難。

使用 ConfigMap ,不但提供一個 Configuration 可以統一存放的地方,也提供一個方法讓開發者可以動態且代碼化的方式為每個應用服務配置其相對應的 Configuration。

什麼是 Configuration

泛指程式存取外部資源或是部署所需的資料,像是 資料庫的所在 IP、管理者的帳號密碼,或是 Nginx 的設定檔 等等。若是其他人不小心存取資源,導致資料庫被刪除,或被公開,都可能導致專案陷入危險之中,或是損害該應用服務的使用者的權益

ConfigMap 與 Secret 的差別

Secret 與 ConfigMap 想解決的面向不太相同,我們可以將機密的資料存在 Secret 中,且 Secret 會將這些值經過 Base64 加密,機密的資料像是 API 或是 database 的密碼;而將非機密但屬於部署面的資料放在 ConfigMap,好比資料庫的 port number 或是 Redis 的 config file

介紹 ConfigMap

  • 一個 ConfigMap 物件可以存入整個 configuration 像是 webserver config file, Nginx config file
  • 無需修改 container 程式碼,可以替換不同環境的 Config 開發過程中,常因應不同的環境需配置不同的 configuration,像是 staging 與 production 存取的資料庫位址不一致等等。無需修改程式碼的特點,可以幫助我們更快部署到各個不同的環境中
  • 統一存放所有的 configuration 透過 kubectl get 指令快速查看目前系統所有的 ConfigMap

典型用法

  • 生成為容器內的環境變量
  • 設置容器啟動命令的啟動參數 (須設置為環境變量)
  • 以 Volume 的形式掛載為容器內部的文件和目錄

使用限制

  • 必須在 Pod 之前創建
  • 受 namespace 限制

建置 ConfigMap

1. 匯入整個 config 檔

ConfigMap 提供 --from-file 參數,讓我們可以存入整個檔案,以 Redis config file 為例

my-redis.conf
bind 127.0.0.1
port 6379
maxclients 10000
maxmemory 50mb
maxmemory-policy volatile-lru
syslog-enabled yes
dir /var/lib/redis
dbfilename redis.dump.rdb
databases 1
appendfsync everysec
save 600 10

透過 kubectl create 創建,

kubectl create configmap redis-config --from-file=my-redis.conf
configmap "redis-config" created

kubectl get configmap
NAME               DATA   AGE
kube-root-ca.crt 1 6h19m
redis-config 1 84s

可以看到我們剛剛創建好的 redis-config ,如果用 kubectl describe 可以看到該物件詳細資訊

kubectl describe
kubectl describe configmap redis-config
Name: redis-config
Namespace: default
Labels: <none>
Annotations: <none>

Data
====
my-redis.conf:
----
bind 127.0.0.1
port 6379
maxclients 10000
maxmemory 50mb
maxmemory-policy volatile-lru
syslog-enabled yes
dir /var/lib/redis
dbfilename redis.dump.rdb
databases 1
appendfsync everysec
save 600 10

BinaryData
====

Events: <none>

2. 透過指令設定 Config

而我們也可以在 kubectl create 指令中,直接設定 ConfigMap 的值

kubectl create
kubectl create configmap mysql-host --from-literal=ip=127.0.0.1
configmap "mysql-host" created
kubectl describe configmap mysql-host
Name: mysql-host
Namespace: default
Labels: <none>
Annotations: <none>

Data
====
ip:
----
127.0.0.1

BinaryData
====

Events: <none>

移除掉某個特定的 ConfigMap 物件,可以使用 kubectl delete 來刪除

kubectl delete configmap mysql-host
configmap "mysql-host" deleted

  1. 匯入整個 YAML 檔案
Config YAML
 apiVersion:  v1
kind: ConfigMap
metadata:
name: appconfig
data:
bind: 127.0.0.1
port: 6379
maxclients: 10000
maxmemory: 50mb
maxmemory-policy: volatile-lru
syslog-enabled: yes
dir: /var/lib/redis
dbfilename: redis.dump.rdb
databases: 1
appendfsync: everysec
save: 600 10

使用 ConfigMaps

Environment Variables
apiVersion:  v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_USER
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
使用 envFrom
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- configMapRef:
name: appconfig
Volumes
apiVersion: v1
kind: Pod
metadata:
name: pod-env
spec:
volumes:
- name: appconfig
configMap:
name: appconfig
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "while true; do echo $(date) >> /tmp/index.html; sleep 10; done"]
volumeMounts:
- name: appconfig
mountPath: "/etc/appconfig"

實作:如何透過 ConfigMap 配置 Nginx

什麼是 Nginx

Nginx 不只是一套輕量的 HTTP 伺服器,同時也是一個反向代理的伺服器。收到用戶的請求後,可以將流量導給後端的 service,再將後端處理好的資源回傳給前端使用者。

Nginx 配置檔案

  • my-pod.yaml
kubectl config view
apiVersion: v1
kind: Pod
metadata:
name: apiserver
labels:
app: webserver
tier: backend
spec:
containers:
- name: nodejs-app
image: zxcvbnius/docker-demo
ports:
- containerPort: 3000
- name: nginx
image: nginx:1.13
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-conf-volume
configMap:
name: nginx-conf
items:
- key: my-nginx.conf
path: my-nginx.conf
my-nginx.conf
server {
listen 80;
server_name localhost;

location / {
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

建立 ConfigMap 與 Pod

kubectl create configmap nginx-conf --from-file=my-nginx.conf
configmap/nginx-conf created
kubectl create -f my-pod.yaml
pod/my-pod created

Nginx 與 API server 的配置

kubectl get pods
NAME READY STATUS RESTARTS AGE
apiserver 2/2 Running 0 17s

確定狀態後,使用 kubectl expose ,指定將 port 80 對應到 minikube 上的某一個 port,

$ kubectl expose pod apiserver --port=80 --type=NodePort
service "apiserver" exposed